/*
@desc webpack 的预加载插件
@author Ocean(2240970215@qq.com)
@params 
@include 可设置的参数: asyncChunks allAssets allChunks
@rel     可设置的参数: preload prefetch differ
*/


'use strict'

const _ = require('lodash')

// 常量
const PLUGIN_NAME = 'pre-loadOrFetch-webpack-plugin'

// 默认配置
const defaultOptions = {
  include: 'allAssets', // 'asyncChunks' allAssets allChunks
  rel: 'differ', // preload prefetch differ
  fileBlacklist: [/\.map/],
}
let asyncChunks = [] // 异步chunks
let synchroChunks = [] // 同步chunks

const _reduce = arr => arr.reduce((prev, curr) => prev.concat(curr))

class CoPrePlugin {
  constructor(options) {
    this.options = Object.assign({}, defaultOptions, options)
  }
  apply(compiler) {
    this.beforeV4 = !compiler.hooks
    if (this.beforeV4) { // v4 之前版本
      compiler.plugin('compilation', this.pluginHandle.bind(this)) // bind(this) 绑定函数的作用域
    } else {
      compiler.hooks['compilation'].tap(PLUGIN_NAME, this.hooksHandle.bind(this))
    }
  }
  pluginHandle(compilation) {
    compilation.plugin("html-webpack-plugin-after-html-processing", (htmlPluginData, cb) => {
      this.afterHtmlProcessing(htmlPluginData, cb, compilation)
    })
  }

  afterHtmlProcessing(htmlPluginData, cb, compilation) {
    // 获取对应的chunks
    const extractedChunks = this.beforeV4
      ? this.extractedChunksbeforeV4(compilation)
      : this.extractedChunksV4(compilation)

    this.addLinks2Html(extractedChunks, compilation, htmlPluginData, cb)
  }

  extractedChunksbeforeV4(compilation, include) {
    const options = this.options
    let extractedChunks = []

    const _include = include || options.include

    if (_include === 'asyncChunks' || _include === undefined) {
      try {
        extractedChunks = compilation.chunks.filter(chunk => !chunk.isInitial()) // 入口文件都是默认首次加载的，即 isInitial为true 和 require.ensure 按需加载是完全不一样的
      } catch (e) {
        extractedChunks = compilation.chunks
      }
    } else if (_include === 'synchroChunks') {
      try {
        extractedChunks = compilation.chunks.filter(chunk => chunk.isInitial()) // 入口文件都是默认首次加载的，即 isInitial为true 和 require.ensure 按需加载是完全不一样的
      } catch (e) {
        extractedChunks = compilation.chunks
      }
    } else if (_include === 'allChunks' || _include === 'all') {
      _include === 'all' && console.warn('[WARNING]: {include: all } is deprecated, please use allChunks instead')
      extractedChunks = compilation.chunks
    } else if (_include === 'allAssets') {

      asyncChunks = _reduce(this.extractedChunksbeforeV4(compilation, 'asyncChunks').map(chunk => chunk.files))
        .filter(entry => options.fileBlacklist.every(reg => reg.test(entry) === false))

      synchroChunks = _reduce(this.extractedChunksbeforeV4(compilation, 'synchroChunks').map(chunk => chunk.files))
        .filter(entry => options.fileBlacklist.every(reg => reg.test(entry) === false))

      const files = Object.keys(compilation.assets)
      extractedChunks = [{ files }]
    }

    return extractedChunks
  }

  addLinks2Html(extractedChunks, compilation, htmlPluginData, cb) {
    const options = this.options
    let file2Head = ''
    const publicPath = compilation.outputOptions.publicPath || ''

    _reduce(extractedChunks.map(chunk => chunk.files))
      .filter(entry => options.fileBlacklist.every(reg => reg.test(entry) === false)) // every（）方法必须所有都返回true才会返回true
      .forEach(path => {
        const entry = publicPath + path

        if (options.rel === 'differ') {
          // 异步chunks 使用prefetch
          if (_.includes(asyncChunks, path)) {
            file2Head += this.preHandle(entry, 'prefetch')
          }

          // 同步chunks 使用preload
          if (_.includes(synchroChunks, path)) {
            file2Head += this.preHandle(entry, 'preload')
          }
        } else if (options.rel === 'preload') {
          file2Head += this.preHandle(entry, 'preload')
        } else if (options.rel === 'prefetch') {
          file2Head += this.preHandle(entry, 'prefetch')
        }
      })

    let _html = htmlPluginData.html
    if (_html.indexOf('</head>') > -1) {
      _html = _html.replace('</head>', `${file2Head}</head>`)
    } else {
      _html = _html.replace('<body>', `<head>${file2Head}</head>`)
    }

    htmlPluginData.html = _html

    cb(null, htmlPluginData)
  }

  preHandle(path, type) {
    const options = this.options
    let url = ''
    if (type === 'preload') {
      let asValue

      if (!options.as) {
        if (path.match(/\.css$/)) asValue = 'style'
        else if (path.match(/\.woff2$/)) asValue = 'font'
        else asValue = 'script'
      } else {
        asValue = options.as
      }
      // 请求跨域时 加上crossorigin 避免二次加载 特别是font
      const CrossOrigin = asValue === 'font' ? 'crossorigin="crossorigin" ' : ''
      url = `<link rel="${type}" as="${asValue}" ${CrossOrigin}href="${path}"> \n`
    } else {
      url = `<link rel="${type}" href="${path}"> \n`
    }
    return url
  }

  extractedChunksV4() {

  }

  // v4
  hooksHandle(compilation) {

  }
}

module.exports = CoPrePlugin
